#include "Font.h"
#include <dxgiformat.h>
#include "math.h"
#include <math.h>
namespace NBGUI
{
	inline int next_p2 ( int a )
	{
		int rval=1;
		while(rval<a) rval<<=1;
		return rval;
	}

	Font::Font( const char* fontPath, RendererBase* renderer, unsigned int h, unsigned int device_w, unsigned int device_h )
		:width_char(0),height_char(0),vboId(-1),iboId(-1)
	{
		try
		{
			this->h = h;
			this->pRenderer = renderer;
			if ( FT_Init_FreeType( &library ) )
			{
				throw GUIException(InitFontLibError,L"Init free type 2 error!");
			}

			FT_Error error =  FT_New_Face( library, fontPath, 0, &face );
			if(error == FT_Err_Unknown_File_Format)
				throw GUIException(InitFontFaceError,L"The font file could be opened and read!");
			else if(error)
				throw GUIException(InitFontFaceError,L"Init font error!");


			FT_Set_Char_Size( face, h << 6, h << 6, device_w, device_h);

			//create textures
			const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ,./<>?;':\"[]\\{}|~!@#$%^&*()_+-=";
			//texArray = new unsigned int[strlen(str)];
			

		 	for (unsigned int i = 0; i < strlen(str);i++)
		 	{
		 		calculateCharWH(str[i]);
		 	}
			float tempSqrt = sqrt((float)strlen(str));
			texNum = tempSqrt > int(tempSqrt)? int(tempSqrt) + 1 : int(tempSqrt) ;

			expanded_data = new byte[ 4 * width_char * height_char * texNum * texNum];
			ZeroMemory(expanded_data,4 * width_char * height_char * texNum * texNum);

			for (unsigned int i = 0; i < strlen(str);i++)
			{
				packCharacter(str[i],i,strlen(str));
			}

			fontTexId = pRenderer->createTexture(L"",expanded_data,width_char * texNum,height_char * texNum,DXGI_FORMAT_R8G8B8A8_UNORM,0,4);
			

			FT_Done_Face(face);
			 
			FT_Done_FreeType(library);


		}
		catch (GUIException& e)
		{
			e.popErrorWindow();
		}
	}


	void Font::calculateCharWH( char ch )
	{
		if(FT_Load_Char( face, ch, FT_LOAD_RENDER ))
			throw GUIException(FreeTypeRuntimeError,L"FT_Load_Glyph failed");

		FT_Bitmap& bitmap=face->glyph->bitmap;

		int width = next_p2( bitmap.width );
		int height = next_p2( bitmap.rows );

		if (width > width_char) width_char = width;
		if (height > height_char) height_char = height;

	}


	void Font::packCharacter( char ch, int idx, int charCount )
	{
		//Load the Glyph for our character.
		if(FT_Load_Char( face, ch, FT_LOAD_RENDER ))
			throw GUIException(FreeTypeRuntimeError,L"FT_Load_Glyph failed");
 
		FT_Bitmap& bitmap=face->glyph->bitmap;

		int width = width_char; //next_p2( bitmap.width );
		int height = height_char;//next_p2( bitmap.rows );


		int offsetX = idx%texNum;//* 4 * width_char;
		int offsetY = idx/texNum;//* 4 * height_char;
		 
		for(int j=0; j <height;j++) 
		{
			for(int i=0; i < width; i++)
			{
				//expanded_data[4*(i+j*width)]= expanded_data[4*(i+j*width)+1] = 
				//expanded_data[4*(i+j*width)+2]= 
				//locate the data position
				expanded_data[4*(i + offsetX*width_char + (offsetY * height_char + j)* texNum * width_char )+3]=
					(i>=bitmap.width || j>=bitmap.rows) ?
					0 : bitmap.buffer[i + bitmap.width*j];
			}
		}
		
		float	x=((float)bitmap.width + offsetX*width_char)/ (texNum * width_char),
			    y=((float)bitmap.rows + offsetY*height_char) / (texNum * height_char);

		float x0=((float)offsetX*width_char)/ (texNum * width_char),
			  y0=((float)offsetY*height_char) / (texNum * height_char);
		
		
		
		

		CharInfo ci;
		ci.ch =ch;
		ci.inedx = idx;
		ci.uv[0] = vec2f(x0,y0);
		ci.uv[1] = vec2f(x0,y);
		ci.uv[2] = vec2f(x,y);
		ci.uv[3] = vec2f(x,y0);
		ci.adv_x = face->glyph->advance.x >> 6;
		ci.adv_y = face->glyph->advance.y >> 6;
		ci.size_x = face->glyph->bitmap.width;
		ci.size_y = face->glyph->bitmap.rows;
		ci.offsetBearing   = (face->glyph->metrics.height - face->glyph->metrics.horiBearingY) >> 6;
		if (ch == ' ')
		{
			ci.size_x = ci.size_y = ci.adv_x;
		}
		charMap[ch] = ci;
	}
 

	strDrawingPack Font::addText( const char* str, float x, float y, float size)
	{
		 unsigned int startIdx = indices.size();
		 unsigned int len = 0;
		 float scale = 0;
		 for(size_t i=0;i<strlen(str);i++) 
		 {
			 char ch = str[i];
			 Vertex eleVertices[4];
			 unsigned int currentVertexNum = vertices.size();
			 unsigned int eleIndices[6]={0,1,2,0,2,3};
			 float left, right, top, bottom;

			 if (scale == 0)
			 {
				  scale = size/80.0f;
			 }
			
			 left = -(float)pRenderer->screenWidth / 2 + x; 
			 x+= charMap[ch].size_x * scale;
			 right = left + charMap[ch].size_x * scale;

			 bottom = (float)pRenderer->screenHeight / 2 - y - charMap[ch].offsetBearing * scale;
			 top = bottom + charMap[ch].size_y * scale ;
			 //top = (float)pRenderer->screenHeight / 2 - y;
			 //bottom = top - size;// * charMap[ch].size_y / charMap[ch].size_x;

			 eleVertices[0].pos = vec3f(left, top, 0.0f);  // Top left.
			 eleVertices[0].coord = charMap[ch].uv[0];

			 eleVertices[1].pos = vec3f(left, bottom, 0.0f);  // Bottom left.
			 eleVertices[1].coord =  charMap[ch].uv[1];

			 eleVertices[2].pos = vec3f(right, bottom, 0.0f);  // Bottom right.
			 eleVertices[2].coord = charMap[ch].uv[2];

			 eleVertices[3].pos = vec3f(right, top, 0.0f);  // Top right.
			 eleVertices[3].coord =  charMap[ch].uv[3];

			 for (int i = 0; i < 4; ++i)
			 {
				 vertices.push_back(eleVertices[i]);
			 }



			 for (int i = 0; i  < 6; ++i)
			 {
				 indices.push_back(currentVertexNum + eleIndices[i]);
			 }

			 len += 6;
			 
		 }
		 return strDrawingPack(startIdx,len);
	}

	void Font::initDrawing()
	{
		if(vboId != -1)
			pRenderer->deleteVBO(vboId);

		vboId = pRenderer->createVBO(&vertices.front(),vertices.size(),sizeof(Vertex),0,"PTC");

		if(iboId != -1)
			pRenderer->deleteIBO(iboId);

		iboId = pRenderer->createIBO(&indices.front(),indices.size());
		
		
	}

	void Font::drawStrPack(strDrawingPack& pack)
	{
		pRenderer->applyTexture(fontTexId);
		pRenderer->drawIndexed(pack.start,pack.len,vboId,iboId);
	}

}